# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\PyInstaller\building\utils.py
import glob, hashlib, os, os.path, pkgutil, platform, shutil, sys
from PyInstaller.config import CONF
from .. import compat
from ..compat import is_darwin, is_win, EXTENSION_SUFFIXES, FileNotFoundError, open_file, is_py3
from ..depend import dylib
from ..depend.bindepend import match_binding_redirect
from ..utils import misc
from ..utils.misc import load_py_data_struct, save_py_data_struct
from .. import log as logging
if is_win:
    from ..utils.win32 import winmanifest, winresource
logger = logging.getLogger(__name__)

def _check_guts_eq(attr, old, new, last_build):
    """
    rebuild is required if values differ
    """
    if old != new:
        logger.info('Building because %s changed', attr)
        return True
    else:
        return False


def _check_guts_toc_mtime(attr, old, toc, last_build, pyc=0):
    """
    rebuild is required if mtimes of files listed in old toc are newer
    than last_build
    
    if pyc=1, check for .py files, too
    
    Use this for calculated/analysed values read from cache.
    """
    for nm, fnm, typ in old:
        if misc.mtime(fnm) > last_build:
            logger.info('Building because %s changed', fnm)
            return True
        if pyc:
            if misc.mtime(fnm[:-1]) > last_build:
                logger.info('Building because %s changed', fnm[:-1])
                return True

    return False


def _check_guts_toc(attr, old, toc, last_build, pyc=0):
    """
    rebuild is required if either toc content changed or mtimes of
    files listed in old toc are newer than last_build
    
    if pyc=1, check for .py files, too
    
    Use this for input parameters.
    """
    return _check_guts_eq(attr, old, toc, last_build) or (_check_guts_toc_mtime(attr, old, toc, last_build, pyc=pyc))


def add_suffix_to_extensions(toc):
    """
    Returns a new TOC with proper library suffix for EXTENSION items.
    """
    from .datastruct import TOC
    new_toc = TOC()
    for inm, fnm, typ in toc:
        if typ == 'EXTENSION':
            if os.path.splitext(inm)[1] not in EXTENSION_SUFFIXES:
                inm = inm + os.path.splitext(fnm)[1]
            else:
                if typ == 'DEPENDENCY':
                    binext = os.path.splitext(fnm)[1]
                    if not os.path.splitext(inm)[1] == binext:
                        inm = inm + binext
            new_toc.append((inm, fnm, typ))

    return new_toc


def applyRedirects(manifest, redirects):
    """
    Apply the binding redirects specified by 'redirects' to the dependent assemblies
    of 'manifest'.
    
    :param manifest:
    :type manifest:
    :param redirects:
    :type redirects:
    :return:
    :rtype:
    """
    for binding in redirects:
        for dep in manifest.dependentAssemblies:
            if match_binding_redirect(dep, binding):
                logger.info('Redirecting %s version %s -> %s', binding.name, dep.version, binding.newVersion)
                dep.version = binding.newVersion


def checkCache(fnm, strip=False, upx=False, dist_nm=None):
    """
    Cache prevents preprocessing binary files again and again.
    
    'dist_nm'  Filename relative to dist directory. We need it on Mac
               to determine level of paths for @loader_path like
               '@loader_path/../../' for qt4 plugins.
    """
    from ..config import CONF
    if not strip:
        if not upx:
            if not is_darwin:
                if not is_win:
                    return fnm
    if dist_nm is not None:
        if ':' in dist_nm:
            return fnm
        if strip:
            strip = True
        else:
            strip = False
        if upx:
            upx = True
        else:
            upx = False
        pyver = 'py%d%s' % (sys.version_info[0], sys.version_info[1])
        arch = platform.architecture()[0]
        cachedir = os.path.join(CONF['cachedir'], 'bincache%d%d_%s_%s' % (strip, upx, pyver, arch))
        if not os.path.exists(cachedir):
            os.makedirs(cachedir)
        cacheindexfn = os.path.join(cachedir, 'index.dat')
        if os.path.exists(cacheindexfn):
            try:
                cache_index = load_py_data_struct(cacheindexfn)
            except Exception as e:
                logger.warn('pyinstaller bincache may be corrupted; use pyinstaller --clean to fix')
                raise

        else:
            cache_index = {}
        if dist_nm:
            basenm = os.path.normcase(dist_nm)
        else:
            basenm = os.path.normcase(os.path.basename(fnm))
        redirects = CONF.get('binding_redirects', [])
        digest = cacheDigest(fnm, redirects)
        cachedfile = os.path.join(cachedir, basenm)
        cmd = None
        if basenm in cache_index:
            if digest != cache_index[basenm]:
                os.remove(cachedfile)
            else:
                if is_darwin:
                    dylib.mac_set_relative_dylib_deps(cachedfile, dist_nm)
                return cachedfile
            if fnm.lower().endswith('.manifest'):
                manifest = winmanifest.Manifest()
                manifest.filename = fnm
                with open(fnm, 'rb') as (f):
                    manifest.parse_string(f.read())
                if CONF.get('win_private_assemblies', False):
                    if manifest.publicKeyToken:
                        logger.info('Changing %s into private assembly', os.path.basename(fnm))
                    manifest.publicKeyToken = None
                    for dep in manifest.dependentAssemblies:
                        if dep.name != 'Microsoft.Windows.Common-Controls':
                            dep.publicKeyToken = None

                applyRedirects(manifest, redirects)
                manifest.writeprettyxml(cachedfile)
                return cachedfile
            if upx:
                if strip:
                    fnm = checkCache(fnm, strip=True, upx=False)
                bestopt = '--best'
                if CONF['hasUPX'] >= (3, ):
                    if os.name == 'nt':
                        bestopt = '--lzma'
                upx_executable = 'upx'
                if CONF.get('upx_dir'):
                    upx_executable = os.path.join(CONF['upx_dir'], upx_executable)
                cmd = [
                 upx_executable, bestopt, '-q', cachedfile]
            else:
                if strip:
                    strip_options = []
                    if is_darwin:
                        strip_options = [
                         '-S']
                    cmd = [
                     'strip'] + strip_options + [cachedfile]
            if not os.path.exists(os.path.dirname(cachedfile)):
                os.makedirs(os.path.dirname(cachedfile))
            shutil.copy(fnm, cachedfile)
    if hasattr(os, 'chflags'):
        try:
            os.chflags(cachedfile, 0)
        except OSError:
            pass

        os.chmod(cachedfile, 493)
        if os.path.splitext(fnm.lower())[1] in ('.pyd', '.dll'):
            try:
                res = winmanifest.GetManifestResources(os.path.abspath(cachedfile))
            except winresource.pywintypes.error as e:
                if e.args[0] == winresource.ERROR_BAD_EXE_FORMAT:
                    pass
                else:
                    logger.error(os.path.abspath(cachedfile))
                    raise
            else:
                if winmanifest.RT_MANIFEST in res:
                    if len(res[winmanifest.RT_MANIFEST]):
                        for name in res[winmanifest.RT_MANIFEST]:
                            for language in res[winmanifest.RT_MANIFEST][name]:
                                try:
                                    manifest = winmanifest.Manifest()
                                    manifest.filename = (':').join([cachedfile,
                                     str(winmanifest.RT_MANIFEST),
                                     str(name),
                                     str(language)])
                                    manifest.parse_string(res[winmanifest.RT_MANIFEST][name][language], False)
                                except Exception as exc:
                                    logger.error('Cannot parse manifest resource %s, %s', name, language)
                                    logger.error('From file %s', cachedfile, exc_info=1)
                                else:
                                    if CONF.get('win_private_assemblies', False):
                                        if manifest.publicKeyToken:
                                            logger.info('Changing %s into a private assembly', os.path.basename(fnm))
                                        manifest.publicKeyToken = None
                                        for dep in manifest.dependentAssemblies:
                                            if dep.name != 'Microsoft.Windows.Common-Controls':
                                                dep.publicKeyToken = None

                                applyRedirects(manifest, redirects)
                                try:
                                    manifest.update_resources(os.path.abspath(cachedfile), [
                                     name], [
                                     language])
                                except Exception as e:
                                    logger.error(os.path.abspath(cachedfile))
                                    raise

    if cmd:
        try:
            logger.info('Executing - ' + (' ').join(cmd))
            compat.exec_command(*cmd)
        except OSError as e:
            raise SystemExit('Execution failed: %s' % e)

        cache_index[basenm] = digest
        save_py_data_struct(cacheindexfn, cache_index)
        if is_darwin:
            dylib.mac_set_relative_dylib_deps(cachedfile, dist_nm)
        return cachedfile


def cacheDigest(fnm, redirects):
    data = open(fnm, 'rb').read()
    hasher = hashlib.md5(data)
    if redirects:
        redirects = str(redirects)
        if is_py3:
            redirects = redirects.encode('utf-8')
        hasher.update(redirects)
    digest = bytearray(hasher.digest())
    return digest


def _check_path_overlap(path):
    """
    Check that path does not overlap with WORKPATH or SPECPATH (i.e.
    WORKPATH and SPECPATH may not start with path, which could be
    caused by a faulty hand-edited specfile)
    
    Raise SystemExit if there is overlap, return True otherwise
    """
    from ..config import CONF
    specerr = 0
    if CONF['workpath'].startswith(path):
        logger.error('Specfile error: The output path "%s" contains WORKPATH (%s)', path, CONF['workpath'])
        specerr += 1
    if CONF['specpath'].startswith(path):
        logger.error('Specfile error: The output path "%s" contains SPECPATH (%s)', path, CONF['specpath'])
        specerr += 1
    if specerr:
        raise SystemExit('Error: Please edit/recreate the specfile (%s) and set a different output name (e.g. "dist").' % CONF['spec'])
    return True


def _make_clean_directory(path):
    """
    Create a clean directory from the given directory name
    """
    if _check_path_overlap(path):
        pass
    if os.path.isdir(path):
        try:
            os.remove(path)
        except OSError:
            _rmtree(path)

        os.makedirs(path)


def _rmtree(path):
    """
    Remove directory and all its contents, but only after user confirmation,
    or if the -y option is set
    """
    from ..config import CONF
    if CONF['noconfirm']:
        choice = 'y'
    else:
        if sys.stdout.isatty():
            choice = compat.stdin_input('WARNING: The output directory "%s" and ALL ITS CONTENTS will be REMOVED! Continue? (y/n)' % path)
        else:
            raise SystemExit('Error: The output directory "%s" is not empty. Please remove all its contents or use the -y option (remove output directory without confirmation).' % path)
        if choice.strip().lower() == 'y':
            logger.info('Removing dir %s', path)
            shutil.rmtree(path)
        else:
            raise SystemExit('User aborted')


def format_binaries_and_datas(binaries_or_datas, workingdir=None):
    """
    Convert the passed list of hook-style 2-tuples into a returned set of
    `TOC`-style 2-tuples.
    
    Elements of the passed list are 2-tuples `(source_dir_or_glob, target_dir)`.
    Elements of the returned set are 2-tuples `(target_file, source_file)`.
    For backwards compatibility, the order of elements in the former tuples are
    the reverse of the order of elements in the latter tuples!
    
    Parameters
    ----------
    binaries_or_datas : list
        List of hook-style 2-tuples (e.g., the top-level `binaries` and `datas`
        attributes defined by hooks) whose:
        * First element is either:
          * A glob matching only the absolute or relative paths of source
            non-Python data files. The second element is then either:
            * The relative path of the target directory into which these source
              files will be recursively copied.
            * The empty string, in which case these source files will be
              recursively copied into the top-level target directory. (This is
              usually _not_ what you want.)
          * The absolute or relative path of a source directory containing only
            source non-Python data files. The second element is then either:
            * The relative path of the target directory into which these source
              files will be recursively copied.
            * The empty string, in which case these source files will be
              recursively copied into a new target subdirectory whose name is
              this source directory's basename. (This is usually what you want.)
        If the optional `workingdir` parameter is passed, source paths may be
        either absolute or relative; else, source paths _must_ be absolute.
    workingdir : str
        Optional absolute path of the directory to which all relative source
        paths in the `binaries_or_datas` parameter will be prepended by (and
        hence converted into absolute paths) _or_ `None` if these paths are to
        be preserved as relative. Defaults to `None`.
    
    Returns
    ----------
    set
        Set of `TOC`-style 2-tuples whose:
        * First element is the absolute or relative path of a target file.
        * Second element is the absolute or relative path of the corresponding
          source file to be copied to this target file.
    """
    toc_datas = set()
    for src_root_path_or_glob, trg_root_dir in binaries_or_datas:
        if workingdir:
            if not os.path.isabs(src_root_path_or_glob):
                src_root_path_or_glob = os.path.join(workingdir, src_root_path_or_glob)
        src_root_path_or_glob = os.path.normpath(src_root_path_or_glob)
        if os.path.isfile(src_root_path_or_glob):
            src_root_paths = [
             src_root_path_or_glob]
        else:
            src_root_paths = glob.glob(src_root_path_or_glob)
        if not src_root_paths:
            raise SystemExit('Unable to find "%s" when adding binary and data files.' % src_root_path_or_glob)
        for src_root_path in src_root_paths:
            if os.path.isfile(src_root_path):
                toc_datas.add((
                 os.path.normpath(os.path.join(trg_root_dir, os.path.basename(src_root_path))),
                 os.path.normpath(src_root_path)))
            elif os.path.isdir(src_root_path):
                if not trg_root_dir:
                    trg_root_dir = os.path.basename(src_root_path)
                for src_dir, src_subdir_basenames, src_file_basenames in os.walk(src_root_path):
                    if not src_dir.startswith(src_root_path):
                        raise AssertionError
                    trg_dir = os.path.normpath(os.path.join(trg_root_dir, os.path.relpath(src_dir, src_root_path)))
                    for src_file_basename in src_file_basenames:
                        src_file = os.path.join(src_dir, src_file_basename)
                        if os.path.isfile(src_file):
                            toc_datas.add((
                             os.path.normpath(os.path.join(trg_dir, src_file_basename)),
                             os.path.normpath(src_file)))

    return toc_datas


def _load_code(modname, filename):
    path_item = os.path.dirname(filename)
    if os.path.basename(filename).startswith('__init__.py'):
        path_item = os.path.dirname(path_item)
    if os.path.basename(path_item) == '__pycache__':
        path_item = os.path.dirname(path_item)
    importer = pkgutil.get_importer(path_item)
    package, _, modname = modname.rpartition('.')
    if sys.version_info >= (3, 3):
        if hasattr(importer, 'find_loader'):
            loader, portions = importer.find_loader(modname)
        loader = importer.find_module(modname)
    logger.debug('Compiling %s', filename)
    if loader:
        if hasattr(loader, 'get_code'):
            return loader.get_code(modname)
        with open_file(filename, 'rb') as (f):
            source = f.read()
        return compile(source, filename, 'exec')


def get_code_object(modname, filename):
    """
    Get the code-object for a module.
    
    This is a extra-simple version for compiling a module. It's
    not worth spending more effort here, as it is only used in the
    rare case if outXX-Analysis.toc exists, but outXX-PYZ.toc does
    not.
    """
    try:
        if filename in ('-', None):
            logger.debug('Compiling namespace package %s', modname)
            txt = '#\n'
            return compile(txt, filename, 'exec')
        logger.debug('Compiling %s', filename)
        co = _load_code(modname, filename)
        if not co:
            raise ValueError('Module file %s is missing' % filename)
        return co
    except SyntaxError as e:
        print('Syntax error in ', filename)
        print(e.args)
        raise


def strip_paths_in_code--- This code section failed: ---

 608       0  LOAD_GLOBAL              'sys'
           2  LOAD_ATTR                'path'
           4  LOAD_GLOBAL              'CONF'
           6  LOAD_CONST               'pathex'
           8  BINARY_SUBSCR    
          10  BINARY_ADD       
          12  STORE_FAST               'replace_paths'

 610      14  LOAD_LISTCOMP            '<code_object <listcomp>>'
          16  LOAD_CONST               'strip_paths_in_code.<locals>.<listcomp>'
          18  MAKE_FUNCTION_0          ''
          20  LOAD_FAST                'replace_paths'
          22  GET_ITER         
          24  CALL_FUNCTION_1       1  ''
          26  STORE_FAST               'replace_paths'

 612      28  LOAD_DEREF               'new_filename'
          30  LOAD_CONST               None
          32  COMPARE_OP               'is'
          34  POP_JUMP_IF_FALSE    96  'to 96'

 613      36  LOAD_GLOBAL              'os'
          38  LOAD_ATTR                'path'
          40  LOAD_ATTR                'normpath'
          42  LOAD_FAST                'co'
          44  LOAD_ATTR                'co_filename'
          46  CALL_FUNCTION_1       1  ''
          48  STORE_FAST               'original_filename'

 614      50  SETUP_LOOP           96  'to 96'
          52  LOAD_FAST                'replace_paths'
          54  GET_ITER         
          56  FOR_ITER             90  'to 90'
          58  STORE_FAST               'f'

 615      60  LOAD_FAST                'original_filename'
          62  LOAD_ATTR                'startswith'
          64  LOAD_FAST                'f'
          66  CALL_FUNCTION_1       1  ''
          68  POP_JUMP_IF_FALSE    56  'to 56'

 616      70  LOAD_FAST                'original_filename'
          72  LOAD_GLOBAL              'len'
          74  LOAD_FAST                'f'
          76  CALL_FUNCTION_1       1  ''
          78  LOAD_CONST               None
          80  BUILD_SLICE_2         2  ''
          82  BINARY_SUBSCR    
          84  STORE_DEREF              'new_filename'

 617      86  BREAK_LOOP       
        88_0  COME_FROM            68  '68'
          88  JUMP_BACK            56  'to 56'
          90  POP_BLOCK        

 620      92  LOAD_FAST                'co'
          94  RETURN_END_IF    
        96_0  COME_FROM_LOOP       50  '50'

 622      96  LOAD_GLOBAL              'type'
          98  LOAD_FAST                'co'
         100  CALL_FUNCTION_1       1  ''
         102  STORE_DEREF              'code_func'

 624     104  LOAD_GLOBAL              'tuple'

 625     106  LOAD_CLOSURE             'code_func'
         108  LOAD_CLOSURE             'new_filename'
         110  BUILD_TUPLE_2         2  ''
         112  LOAD_GENEXPR             '<code_object <genexpr>>'
         114  LOAD_CONST               'strip_paths_in_code.<locals>.<genexpr>'
         116  MAKE_FUNCTION_8          'closure'

 627     118  LOAD_FAST                'co'
         120  LOAD_ATTR                'co_consts'
         122  GET_ITER         
         124  CALL_FUNCTION_1       1  ''
         126  CALL_FUNCTION_1       1  ''
         128  STORE_FAST               'consts'

 631     130  LOAD_GLOBAL              'hasattr'
         132  LOAD_FAST                'co'
         134  LOAD_CONST               'co_kwonlyargcount'
         136  CALL_FUNCTION_2       2  ''
         138  POP_JUMP_IF_FALSE   202  'to 202'

 632     140  LOAD_DEREF               'code_func'
         142  LOAD_FAST                'co'
         144  LOAD_ATTR                'co_argcount'
         146  LOAD_FAST                'co'
         148  LOAD_ATTR                'co_kwonlyargcount'
         150  LOAD_FAST                'co'
         152  LOAD_ATTR                'co_nlocals'
         154  LOAD_FAST                'co'
         156  LOAD_ATTR                'co_stacksize'

 633     158  LOAD_FAST                'co'
         160  LOAD_ATTR                'co_flags'
         162  LOAD_FAST                'co'
         164  LOAD_ATTR                'co_code'
         166  LOAD_FAST                'consts'
         168  LOAD_FAST                'co'
         170  LOAD_ATTR                'co_names'

 634     172  LOAD_FAST                'co'
         174  LOAD_ATTR                'co_varnames'
         176  LOAD_DEREF               'new_filename'
         178  LOAD_FAST                'co'
         180  LOAD_ATTR                'co_name'

 635     182  LOAD_FAST                'co'
         184  LOAD_ATTR                'co_firstlineno'
         186  LOAD_FAST                'co'
         188  LOAD_ATTR                'co_lnotab'

 636     190  LOAD_FAST                'co'
         192  LOAD_ATTR                'co_freevars'
         194  LOAD_FAST                'co'
         196  LOAD_ATTR                'co_cellvars'
         198  CALL_FUNCTION_15     15  ''
         200  RETURN_END_IF    

 638     202  LOAD_DEREF               'code_func'
         204  LOAD_FAST                'co'
         206  LOAD_ATTR                'co_argcount'
         208  LOAD_FAST                'co'
         210  LOAD_ATTR                'co_nlocals'
         212  LOAD_FAST                'co'
         214  LOAD_ATTR                'co_stacksize'

 639     216  LOAD_FAST                'co'
         218  LOAD_ATTR                'co_flags'
         220  LOAD_FAST                'co'
         222  LOAD_ATTR                'co_code'
         224  LOAD_FAST                'consts'
         226  LOAD_FAST                'co'
         228  LOAD_ATTR                'co_names'

 640     230  LOAD_FAST                'co'
         232  LOAD_ATTR                'co_varnames'
         234  LOAD_DEREF               'new_filename'
         236  LOAD_FAST                'co'
         238  LOAD_ATTR                'co_name'

 641     240  LOAD_FAST                'co'
         242  LOAD_ATTR                'co_firstlineno'
         244  LOAD_FAST                'co'
         246  LOAD_ATTR                'co_lnotab'

 642     248  LOAD_FAST                'co'
         250  LOAD_ATTR                'co_freevars'
         252  LOAD_FAST                'co'
         254  LOAD_ATTR                'co_cellvars'
         256  CALL_FUNCTION_14     14  ''
         258  RETURN_VALUE     

Parse error at or near `RETURN_END_IF' instruction at offset 94